/***************************************************************
*  Copyright (c) 2008 by GroupMe! Team (www.groupme.net)
*  All rights reserved.
*
*  This file is part of the GroupMe! Project. Source code of 
*  this project is closed and redistribution of this code is
*  prohibited. 
*  
*  Contact: http://www.groupme.net
*
*  This copyright notice MUST APPEAR in all copies of the file!
***************************************************************/
package net.groupme.logging.utils;

import java.sql.Types;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

import net.groupme.gui.constants.VIEW_VOCABULARY;
import net.groupme.logging.GroupMeLoggingPropertyManager;
import net.groupme.model.ObjectFactory;
import net.groupme.user.UserManager;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.object.SqlUpdate;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.util.WebUtils;

/**
 * created on Oct 23, 2008
 * 
 * This class defines methods for logging/tracking the user. It, e.g., updates the
 * LogGeneral table, which will be created in case it is not in the database yet.
 * 
 * @author Fabian Abel, <a href="mailto:abel@l3s.de">abel@l3s.de</a>
 * @author last edited by: $Author: fabian $
 * 
 * @version $Revision: 1.1 $ $Date: 2008-10-24 10:22:27 $
 */
public class UserTrackerUtility {

	/** Logger for this class */
	protected static final Log logger = LogFactory.getLog(UserTrackerUtility.class);
	
	private static boolean loggingEnabled = false;
	static{
		try{
			loggingEnabled = Boolean.parseBoolean(GroupMeLoggingPropertyManager.getString("groupme.usertracking.enabled")); //$NON-NLS-1$
		}catch(Exception e){
			logger.info("Failed to read in the property 'groupme.usertracking.enabled=true' from /GroupMe/log/net/groupme/logging/groupmelogging.properties");
		}
	}
	
	/**
	 * Generates a new client code that, which looks as follows:
	 * <pre>
	 * hash(HTTP.header.user-agent) - hash(HTTP.header.accept) - current_time_in_millis
	 * </pre>
	 * Example:
	 * <pre>
	 * 889283366-663697213-1224772933660
	 * </pre>
	 * @param request the Http request.
	 * @return a new client code generated from the HTTP header and the current time.
	 */
	@SuppressWarnings("unchecked")
	public static final String generateNewClientCode(HttpServletRequest request){
		String clientCode = "";
		//user-agent:
		if(request.getHeader("user-agent") != null){
			clientCode += request.getHeader("user-agent").hashCode();
		}
		clientCode += "-";
		//accept:
		if(request.getHeader("accept") != null){
			clientCode += request.getHeader("accept").hashCode();
		}
		clientCode += "-" + System.currentTimeMillis();
		return clientCode;
	}
	
	/**
	 * Simple logging of the URL, the clientCode, and (possibly) the userId
	 * @param request
	 */
	public static final void logHttpRequest(HttpServletRequest request){
		if(loggingEnabled){
			//User Tracking:
			String clientCode = null;
			Cookie trackerCookie = WebUtils.getCookie(request, VIEW_VOCABULARY.COOKIE_FOR_USER_TRACKING);
			if(trackerCookie != null){
				clientCode = trackerCookie.getValue();
			}
			String userID = null;
			try{
			UserManager userManager = (UserManager) WebApplicationContextUtils.getWebApplicationContext(request.getSession().getServletContext()).getBean(VIEW_VOCABULARY.USER_MANGER, UserManager.class);	
			if(userManager.isLoggedIn()){
				userID = userManager.getUser().getId();
			}
			}catch(Exception e){
				//then we don't care..
			}
			String action = request.getRequestURI();
			if(request.getQueryString() != null){
				action += "?" + request.getQueryString();
			}
			try{
				writeToDatabase(userID, clientCode, action);
			}catch(Exception e){
				//maybe the DB table does not exist:
				try{
					createLogGeneralDBTable();
					//try again:
					writeToDatabase(userID, clientCode, action);
				}catch (Exception e2){
						logger.info("Logging for user tracking failed. Failed to insert into LogGeneral table: " + e2.getMessage());
				}
			}
		}
	}
	/**
	 * Write logging stuff to database.
	 * @param userID the user ID (User.id)
	 * @param clientCode a code that identifies a client although the user is not logged in
	 * @param action the performed action (e.g. /GroupMe/tag/beijing)
	 * @throws Exception
	 */
	private static void writeToDatabase(String userID, String clientCode, String action) throws Exception{
		SqlUpdate su = null;
		if(userID != null && clientCode != null){
			su = new SqlUpdate(ObjectFactory.ds, "INSERT INTO LogGeneral (userId, clientCode, action) VALUES (?, ?, ?)");
			su.declareParameter(new SqlParameter("userId", Types.INTEGER)); //$NON-NLS-1$
			su.declareParameter(new SqlParameter("clientCode", Types.VARCHAR)); //$NON-NLS-1$
			su.declareParameter(new SqlParameter("action", Types.VARCHAR)); //$NON-NLS-1$
			Object[] oa = {Integer.parseInt(userID), clientCode, action};
			su.update(oa);
		}else if(userID == null && clientCode != null){
			su = new SqlUpdate(ObjectFactory.ds, "INSERT INTO LogGeneral (clientCode, action) VALUES (?, ?)");
			su.declareParameter(new SqlParameter("clientCode", Types.VARCHAR)); //$NON-NLS-1$
			su.declareParameter(new SqlParameter("action", Types.VARCHAR)); //$NON-NLS-1$
			Object[] oa = {clientCode, action};
			su.update(oa);
		}else if(userID != null && clientCode == null){
			su = new SqlUpdate(ObjectFactory.ds, "INSERT INTO LogGeneral (userId, action) VALUES (?, ?)");
			su.declareParameter(new SqlParameter("userId", Types.INTEGER)); //$NON-NLS-1$
			su.declareParameter(new SqlParameter("action", Types.VARCHAR)); //$NON-NLS-1$
			Object[] oa = {Integer.parseInt(userID), action};
			su.update(oa);
		}
	}
	private static void createLogGeneralDBTable() {
		String newTable = "CREATE TABLE IF NOT EXISTS LogGeneral ( id int(11) NOT NULL auto_increment,  " +
				" userId int(11) default NULL, " +
				" clientCode varchar(255) default NULL, " +
				" action varchar(512) NOT NULL, timeAndDate " +
				" timestamp NOT NULL default CURRENT_TIMESTAMP,  PRIMARY KEY  (id),  " +
				" KEY userId (userId)) " +
				" ENGINE=InnoDB DEFAULT CHARSET=latin1 " +
				" COMMENT='clientCode = hash value that identifies the client in a cert' AUTO_INCREMENT=0;" ;
		SqlUpdate su = new SqlUpdate(ObjectFactory.ds, newTable);
		su.update();
		String alterTable = "ALTER TABLE LogGeneral ADD CONSTRAINT LogGeneral FOREIGN KEY (userId) REFERENCES User (id)," +
				"  ADD CONSTRAINT LogGeneral_ibfk_1 FOREIGN KEY (userId) REFERENCES User (id)";
		su = new SqlUpdate(ObjectFactory.ds, alterTable);
		su.update();
	}
}
